package workgroup.osaka;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;


public class AlarmActivity extends Activity {    
    
    // ビュー
    private LinearLayout mAddAlarmLayout;
    private ListView mAlarmList;

    private final String TAG = "AlarmActivity";
    private alarmListAdapter ADAPTER = null;
    private AlarmDBHelper m_alarmDBHelper;
    private NextAlarm m_nextAlarm;                  // 次アラーム
    private final int MAXIMUM_ALARM_LIST = 25;      // アラームリスト最大数
    private final int REQUEST_CODE_FOR_DETAIL = 0;  // 詳細画面遷移 識別コード
    private final int REQUEST_CODE_FOR_DO = 1;      // アラーム鳴動画面遷移 識別コード
    private final String H[] = { "2013/3/23"        
    };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 通常画面(main.xml)の表示
        setContentView(R.layout.main);
        
        // ＤＢ生成
        m_alarmDBHelper = new AlarmDBHelper(this);
        
        initView();
    }
    
    @Override
    public void onResume() {
        super.onResume();

        // インテントを取得
        Intent intent = getIntent();
        //アラーム鳴動画面からのリクエストコード確認
        int requestcode_do = intent.getIntExtra("requestcode_do", -1);
        if(requestcode_do == REQUEST_CODE_FOR_DO){
            AlarmInfo info = (AlarmInfo)intent.getSerializableExtra("alarm");
            if(info != null){
                // DBを更新する
                updateAlarmTableFromAlarmInfo(info);
            }
        }

        NextAlarm tmp = getNextAlarmFromAlarmTable();
        if (getNextAlarm() == null) {
            // TODO アプリを落とし、再起動したとき再び同じアラームがセットされる事の是正処置。但し、Intentが上書きされるので２重に起動することはない。
            setNextAlarm(tmp);
        }
    }
    private void initView() {
        mAddAlarmLayout = (LinearLayout)findViewById(R.id.addAlarm_layout);
        mAlarmList = (ListView)findViewById(R.id.alarmList);

        mAddAlarmLayout.setOnClickListener( new OnClickListener() {
            public void onClick(View v) {
                // アラームリスト数が最大数以上の場合はアラーム追加不可を通知
                ListView alarmList = (ListView)findViewById(R.id.alarmList);
                if (alarmList.getCount() <= MAXIMUM_ALARM_LIST ) {
                    startAlarmDetailActivity(new AlarmInfo(v.getContext()));
                } else {
                    String message = "アラームリストがいっぱいです。\n不要なアラームを削除（リスト長押しで動作）してください。";
                    Toast.makeText(AlarmActivity.this, message, Toast.LENGTH_SHORT)
                            .show();
                }
            }
        });

        // アラーム情報をＤＢから取得
        ArrayList<AlarmInfo> list = getAlarmInfoFromAlarmDB();
        ADAPTER = new alarmListAdapter(this, R.layout.alramlist_row, list);
        mAlarmList.setAdapter(ADAPTER);
        // アラームリスト タップ
        mAlarmList.setOnItemClickListener( new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ListView listView = (ListView)parent;
                AlarmInfo info = (AlarmInfo)listView.getItemAtPosition(position);
                startAlarmDetailActivity(info);   
            }
        });
        // アラームリスト 長押し
        mAlarmList.setOnItemLongClickListener( new OnItemLongClickListener() {
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                ListView listView = (ListView)parent;
                AlarmInfo info = (AlarmInfo)listView.getItemAtPosition(position);
                daleteAlarmInfoDialog(info);
                return false;
            }
        });
    }
    /***************************************
     * アラームテーブルの情報をalarmInfoで取得する
     ****************************************/    
    private ArrayList<AlarmInfo> getAlarmInfoFromAlarmDB() {
        SQLiteDatabase db = m_alarmDBHelper.getReadableDatabase();
        ArrayList<AlarmInfo> alarmInfos = null;
        Cursor cursor = null;
        try {
            // アラームテーブルから登録済みアラーム情報を検索する
            cursor = db.query("alarm_table", null, null, null, null, null, "alarmTime");
            // ＤＢの検索結果を読み込む
            alarmInfos = readCursolByAlarmTable(cursor);
        } catch(Exception e) {
            Toast.makeText(AlarmActivity.this, "アラームの参照に失敗しました。", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "DB参照エラー", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            //db.close();
        } 
        return alarmInfos;
    }
    
    /***************************************
     * アラームテーブルから次アラームを取得する
     ****************************************/    
    private NextAlarm getNextAlarmFromAlarmTable() {
        SQLiteDatabase db = m_alarmDBHelper.getReadableDatabase();
        Cursor cursor = null;
        ArrayList<AlarmInfo> alarmInfos = null;
        Calendar nowCal = Calendar.getInstance();
        Calendar nextAlarmCal = null;
        AlarmInfo nextAlarmInfo = null; 
        NextAlarm ret = new NextAlarm(this);
        
        //====================================
        // アラームテーブルからアラーム情報を取得する
        //====================================
        try {
            // アラームONの情報のみ取得する
            String[] cols = null;
            String selection = "alarmOn = ?";
            String selectionArgs[] = {"1"};
            String groupBy = null;
            String having = null;
            String orderBy = "alarmTime";
            cursor = db.query("alarm_table", cols, selection, selectionArgs, groupBy, having, orderBy);
            alarmInfos = readCursolByAlarmTable(cursor);
        } catch(Exception e) {
            Toast.makeText(AlarmActivity.this, "次アラームの検索に失敗しました。", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "DB検索エラー: 次アラーム検索", e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
            //db.close();
        }
        //====================================
        // 次アラームを決定する
        //====================================        
        for (AlarmInfo info: alarmInfos) {           
            Calendar tmpCal = getNearCalendar(nowCal, info.getAlarmTime(), info.getRepeatingDay(), info.getHolidayAlarmStop());
            if (tmpCal == null) { // tmpCalが取得できない場合は次のリストに処理を移動する
                Log.w(TAG, "getNextAlarmFromAlarmTable: " + "tmpCal=null");
                continue;
            }
            if (nextAlarmCal == null || nextAlarmCal.getTimeInMillis() > tmpCal.getTimeInMillis()) {
                nextAlarmCal = tmpCal;
                nextAlarmInfo = info;
                Log.v(TAG, "☆☆" + "nextAlarmCal:" + nextAlarmCal.getTime());
            }
        }
            // 次アラーム設定
        ret.setAlarmCal(nextAlarmCal);
        ret.setAlarmInfo(nextAlarmInfo);
        
        return ret;
    }
    /***************************************
     * アラームテーブル検索結果の読み込み.
     * <p>
     * アラームテーブル検索結果をalarmInfoのリストで返す
     * </p>
     * @return アラームテーブル検索結果のアラーム情報のArrayList
     * @param cursor アラームテーブル検索結果
     ****************************************/
    private ArrayList<AlarmInfo> readCursolByAlarmTable(Cursor cursor) {
        ArrayList<AlarmInfo> ret = new ArrayList<AlarmInfo>();
        cursor.moveToFirst();
        while (cursor.moveToNext()) {
            // アラーム情報に登録
            AlarmInfo info = new AlarmInfo(this);
                    // アラームID
            info.setAlarmId(cursor.getInt(cursor.getColumnIndex("_id")));
                    // アラーム時刻
            info.setAlarmTime(cursor.getString(cursor.getColumnIndex("alarmTime")));
                    // 繰り返し曜日
            info.setRepeatingDay(cursor.getString(cursor.getColumnIndex("repeatingDay")));
                    // スヌーズ間隔
            info.setSnoozeInterval(cursor.getInt(cursor.getColumnIndex("snoozeInterval")));
                    // 祝日鳴動停止（1:true 0:false)
            if (cursor.getInt(cursor.getColumnIndex("holidayAlarmStop")) == 1) {
                info.setHolidayAlarmStop(true);
            } else {
                info.setHolidayAlarmStop(false);
            }
                    // アラーム音量
            info.setAlarmVol(cursor.getInt(cursor.getColumnIndex("alarmVol")));
                    // バイブレーション（1:true 0:false)
            if (cursor.getInt(cursor.getColumnIndex("vibration")) == 1) {
                info.setVibration(true);
            } else {
                info.setVibration(false);
            }
                    // アラームON（1:true 0:false)
            if (cursor.getInt(cursor.getColumnIndex("alarmOn")) == 1) {
                info.setAlarmOn(true);
            } else {
                info.setAlarmOn(false);
            }
            
            // アラーム情報を返値リストに登録
            ret.add(info);
        }
        return ret;
    }

    /***************************************
     * 条件に一致した一番近いカレンダーを取得する.
     * <p>
     * 条件となる時間・曜日・祝日除外に一致する、基準のカレンダーに一番近い未来のカレンダーを取得する
     * </p>
     * @return 条件に一番近い未来のカレンダー
     * @param calendar 基準のカレンダー 
     * @param time 条件の時間　"HH:mm"
     * @param repeatingDay 条件の曜日（=繰り返し曜日） "月火水木金土日"の文字列で除外する曜日は"　"と表す。全ての曜日が"　"の場合は曜日の情報は条件から除外する
     * @param excludeHoliday 祝日を除外するかどうか（=祝日鳴動停止）
     ****************************************/    
    private Calendar getNearCalendar(final Calendar calendar, String time, String repeatingDay, boolean excludeHoliday) {
        Calendar nearCal = Calendar.getInstance();// 基準カレンダーに一番近い未来のカレンダー。初期値は現在時刻
        String[] tmpTime = time.split(":");
        int hourOfDay = Integer.valueOf(tmpTime[0]); // 条件の時間
        int minute = Integer.valueOf(tmpTime[1]);    // 条件の分
        
        //========================================
        // 条件の時間に最近傍の未来のカレンダーを設定する
        //========================================
            // nearCalに条件の時間を設定する
        nearCal.setTimeInMillis(calendar.getTimeInMillis());
        nearCal.set(Calendar.HOUR_OF_DAY, hourOfDay);
        nearCal.set(Calendar.MINUTE, minute);
        nearCal.set(Calendar.SECOND, 0);
            // nearCalが最近傍の未来のカレンダーになるように日にちを調整する
        while (nearCal.getTimeInMillis() <= calendar.getTimeInMillis()) { // nearCalは現在時刻±24時間以内のはずで、whileにする必要はないが念のため
            nearCal.add(Calendar.DATE, 1);
            Log.v(TAG, "getNearCalendar: " + "nearCal: " + nearCal.getTime() + " calendar: " + calendar.getTime());
        }
        
        //==================================================================
        // 繰り返し曜日（repeatingDay）の設定に一致する最近傍の未来のカレンダーを返す
        //==================================================================
            // 繰り返し曜日の設定がない（=曜日の条件がない）ので、時間の条件に一致したnearCalを返す。
            // TODO repeatingDayが「全角空白"　"×７文字」=「繰り返し曜日の設定がない」は直感的に分かりづらく、
            //      repeatingDayの文字列の表記に依存した処理になっている。
            //      表記と内部処理との依存を無くす。
        if (repeatingDay.equals("　　　　　　　")) {
            return nearCal;
        }
                       
            // 繰り返し曜日と祝日鳴動停止設定に一致する最近傍の未来のカレンダーを探す
        while (true) {
            int index = -1; // 繰り返し曜日（月〜日の７文字）の文字列位置（index）。0=月で、0~6が設定される。
                // nearCalの曜日に一致する繰り返し曜日の文字列位置（index）を確定する。
            switch (nearCal.get(Calendar.DAY_OF_WEEK)) {
                case Calendar.MONDAY:   index = 0; break;
                case Calendar.TUESDAY:  index = 1; break;
                case Calendar.WEDNESDAY:index = 2; break;
                case Calendar.THURSDAY: index = 3; break;
                case Calendar.FRIDAY:   index = 4; break;
                case Calendar.SATURDAY: index = 5; break;
                case Calendar.SUNDAY:   index = 6; break;
                default: {
                    // ここにきたらおかしい
                    Log.w(TAG, "getNearCalendar: " + nearCal.get(Calendar.DAY_OF_WEEK));
                    index = -1;
                }
            }
               // nearCalの曜日に一致する繰り返し曜日のindexが取得できなかったため、エラー終了する
            if (index == -1) {
                Log.w(TAG, "getNearCalendar: " + "曜日が正しく取得できなかった" + "index= " + index );
                return null;
            }
                // 最近傍の未来のカレンダーを見つける
            if (repeatingDay.charAt(index) != '　') {  // 繰り返し曜日の設定がある（=nearCalの曜日が条件に一致した）
                    // TODO 祝日鳴動停止設定がある場合、祝日かどうかを調べ、祝日以外の場合はループを抜ける 
                if (true) {
                    break;
                }
            }
                   // 条件に一致しないので１日増やす
            nearCal.add(Calendar.DATE, 1);
        }
        return nearCal;
    }
    
    /****************************************
     * アラームテーブルにアラーム情報を挿入する.
     * <p>
     * info　アラーム情報
     * </p> 
     *****************************************/    
    private void addToAlarmTableFromAlarmInfo (AlarmInfo info) {
        SQLiteDatabase db;
        
        // アラーム情報をＤＢに登録可能な情報に変換する
        ContentValues val = convertAlarmInfoIntoContentValuesForAlarmTable(info);
        
        // alarm_tableに挿入する
        db = m_alarmDBHelper.getWritableDatabase();
        try {
            db.insert("alarm_table", null, val);            
        } catch (Exception e){
            Toast.makeText(AlarmActivity.this, "アラームの挿入に失敗しました", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "DB挿入エラー", e);
        }
        // db.close();
    }
    
    /****************************************
     * アラームテーブルにアラーム情報を更新する.
     * 更新するデータは引数のalarmInfoのidが一致するレコード。
     * 一致するデータがない場合は何もしない
     * <p>
     * info　アラーム情報
     * </p> 
     *****************************************/    
    private void updateAlarmTableFromAlarmInfo (AlarmInfo info) {
        SQLiteDatabase db;
        
        // アラーム情報をにalarm_tableに登録可能なContentValuesに変換する
        ContentValues val = convertAlarmInfoIntoContentValuesForAlarmTable(info);
        
        // ＤＢを更新する
        db = m_alarmDBHelper.getWritableDatabase();
        try {
            // アラームIDと一致するレコードを更新する
            db.update("alarm_table", val, "_id="+info.getAlarmId(), null);            
        } catch (Exception e){
            Toast.makeText(AlarmActivity.this, "アラームの更新に失敗しました", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "DB更新エラー", e);
        }
        // db.close();
    }
    
    /****************************************
     * アラームテーブルをIdを条件に削除する.
     * 削除するレコードは引数のalarmInfoのidが一致するもの。
     * 一致するレコードがない場合は何もしない
     * <p>
     * info　アラーム情報
     * </p> 
     *****************************************/    
    private void deleteAlarmTableById (AlarmInfo info) {
        SQLiteDatabase db;        
        db = m_alarmDBHelper.getWritableDatabase();
        try {
            // アラームIDと一致するレコードを削除する
            db.delete("alarm_table", "_id="+info.getAlarmId(), null);            
        } catch (Exception e){
            Toast.makeText(AlarmActivity.this, "アラームの削除に失敗しました", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "DB削除エラー", e);
        }
        // db.close();
    }

    /****************************************
     * アラーム情報をContentValuesに変換する.
     * 変換後のContentValuesは、alarm_tableに登録可能。
     * <p>
     * info　アラーム情報
     * </p> 
     *****************************************/    
    private ContentValues convertAlarmInfoIntoContentValuesForAlarmTable (AlarmInfo info) {
        String alarmTime, repeatingDay;
        int alarmId, snoozeInterval, alarmVol;
        int holidayAlarmStop;   // 1:祝日に鳴らさない 0:祝日による制約なし
        int vibration;          // 1:バイブレーションON 0:バイブレーションOFF
        int alarmOn;            // 1:アラームON 0:アラームOFF
        
        // アラーム情報をalarm_tableに登録可能な情報に変換する
                // アラームID
        alarmId = info.getAlarmId();
                // アラーム時刻
        alarmTime = info.getAlarmTime();
                // 繰り返し曜日
        repeatingDay = info.getRepeatingDay();
                // スヌーズ間隔
        snoozeInterval = info.getSnoozeInterval();
                // 祝日鳴動停止(true:1 true:0)
        if (info.getHolidayAlarmStop() == true) {
            holidayAlarmStop = 1;
        } else {
            holidayAlarmStop = 0;
        }
                // アラーム音量
        alarmVol = info.getAlarmVol();
                // バイブレーションON(true:1 false:0)
        if (info.getVibration() == true) {
            vibration = 1;
        } else {
            vibration = 0;
        }
                // アラームON(true:1 false:0)
        if (info.getAlarmOn() == true) {
            alarmOn   = 1;
        } else {
            alarmOn   = 0;
        }
        
        // ＤＢに登録する情報を作成
        ContentValues val = new ContentValues();
            // alarmId が-1のときはＤＢで自動で設定出来るように返値に設定しない
        if (alarmId != -1) {  val.put("_id", alarmId); }
        val.put("alarmTime", alarmTime);
        val.put("repeatingDay", repeatingDay);
        val.put("snoozeInterval", snoozeInterval);
        val.put("holidayAlarmStop", holidayAlarmStop);
        val.put("alarmVol", alarmVol);
        val.put("vibration", vibration);
        val.put("alarmOn", alarmOn);
        
        return val;
    }

    /***************************************
     * アラーム情報削除 確認ダイアログ.
     * <p>
     * info 削除するアラーム情報
     * </p>
     *****************************************/
    private void daleteAlarmInfoDialog(final AlarmInfo info) {
        
        // スヌーズ間隔設定ダイアログ生成
        final AlertDialog.Builder deleteAlarmDlg = new AlertDialog.Builder(AlarmActivity.this);

        // 表示メッセージの設定
        deleteAlarmDlg.setMessage(info.getAlarmTime() + "のアラームを\nを削除しますか？");

        // キャンセルボタン 処理
        DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
            }
        };
        deleteAlarmDlg.setNegativeButton(R.string.cancel, cancelListener);
        
        // OK 処理
        DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // アラーム情報をalarm_tableから削除
                deleteAlarmTableById(info);
                // 表示の更新
                updateListView(getAlarmInfoFromAlarmDB());
                // 次アラームの登録・更新
                NextAlarm tmp = getNextAlarmFromAlarmTable();
                setNextAlarm(tmp);                
            }
        };
        deleteAlarmDlg.setPositiveButton("OK", okListener);

        // スヌーズ間隔設定ダイアログ表示
        deleteAlarmDlg.show();
    }
    
    /***************************************
     * アラーム詳細画面（alarm_detail) 遷移処理.
     * <p>
     * アラーム詳細画面に遷移する
     * </p>
     * @param info 詳細画面に送るアラーム情報
     *****************************************/
    private void startAlarmDetailActivity(AlarmInfo info) {
        Intent intent = new Intent(AlarmActivity.this, AlarmDetailActivity.class);
        intent.putExtra("alarm", info);
        try {
            startActivityForResult(intent, REQUEST_CODE_FOR_DETAIL);
        } catch (Exception e) {
            Toast.makeText(AlarmActivity.this, "画面遷移に失敗しました。", Toast.LENGTH_SHORT)
                    .show();
            Log.e(TAG, "詳細画面遷移エラー", e);
        }
    }
    
    /***************************************
     * アラームを設定する.
     * <p>
     * AlarmManagerにアラームとアラーム鳴動画面を設定する。
     * </p>
     * @param alarm 設定する次アラーム
     *****************************************/
    private void startAlarm(NextAlarm alarm) {
        // AlarmManagerの取得
        AlarmManager alarmMng = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
        
        // アラームを登録
        alarmMng.set(AlarmManager.RTC_WAKEUP,
                alarm.getAlarmCal().getTimeInMillis(),
                getPendingIntentOfStartDoAlarm(alarm.getAlarmInfo()));
    }
    
    /****************************************
     * アラーム鳴動画面への画面遷移呼び出し設定の取得（PendingIntent).
     * @param info アラーム鳴動画面へ引き渡すアラーム情報
     *****************************************/
    private PendingIntent getPendingIntentOfStartDoAlarm(AlarmInfo info) {
        // アラーム鳴動画面への遷移インテントを設定する
        Intent intent = new Intent(AlarmActivity.this, DoAlarmActivity.class);
        intent.putExtra("alarm", info);

        // PendingIntent作成
        PendingIntent pendingIntent = PendingIntent.getActivity(this, REQUEST_CODE_FOR_DO, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        return pendingIntent;
    }
    /****************************************
     * 通常画面に戻ってくる結果の受け取り.
     * @param requestCode 他のアクティビティを呼び出したリクエストコード
     * @param resultCode  戻ってきた結果コード
     * @param intent      戻ってきたインテント
     *****************************************/
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        Log.v(TAG, "★★ " + requestCode + " " + resultCode + " " + intent);
        if (requestCode == REQUEST_CODE_FOR_DETAIL) {
            // アラーム詳細画面から戻ってきた
            
            if (resultCode == RESULT_OK) {
                AlarmInfo info = (AlarmInfo)intent.getSerializableExtra("alarm");
                
                // 戻り値のalarm_infoをＤＢに登録する
                //   alarm_infoのidが0以下の場合...ＤＢへ挿入
                //   alarm_infoのidが1以上の場合...ＤＢを更新
                if (info.getAlarmId() <= 0) {
                    addToAlarmTableFromAlarmInfo(info);
                } else {
                    updateAlarmTableFromAlarmInfo(info);
                }
                // アラームリストの更新
                updateListView(getAlarmInfoFromAlarmDB());

                // 次アラームの登録・更新
                NextAlarm tmp = getNextAlarmFromAlarmTable();
                setNextAlarm(tmp);
            }
        } else {
            Log.v(TAG, "onActivityResult: リクエストコードがおかしい " + "requestCode:"+ resultCode + "resultCode:" + resultCode + "intent" + intent);
        }
    }

    /***************************************
     * アラームリスト Adapter.
     ****************************************/
    private class alarmListAdapter extends ArrayAdapter<AlarmInfo> {
        private ArrayList<AlarmInfo> ITEMS;
        private LayoutInflater inflater;
        public alarmListAdapter(Context context, int textViewResourceId, ArrayList<AlarmInfo> items) {
            super(context, textViewResourceId, items);
            ITEMS = items;
            inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        } 
        
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = convertView;
            final AlarmInfo ITEM = (AlarmInfo)ITEMS.get(position);

            if ( view == null ) {
                // 新しくビューを作成
                view = inflater.inflate(R.layout.alramlist_row, null);
            }
            
            if ( ITEM != null ) {
                // アラーム時刻の情報をビューに設定する
                TextView alarmTime = (TextView)view.findViewById(R.id.alarmTime);
                if ( alarmTime != null ) {
                    alarmTime.setText(ITEM.getAlarmTime());
                }
                
                // 繰り返し曜日の情報をビューに設定する
                TextView repeatingDay = (TextView)view.findViewById(R.id.repeatingDay);
                if ( repeatingDay != null ) {
                    repeatingDay.setText(ITEM.getRepeatingDay());
                }
                
                // 祝日鳴動停止の情報をビューに設定する
                RelativeLayout holidayAlarmStop = (RelativeLayout)view.findViewById(R.id.holidayAlarmStop);
                if ( holidayAlarmStop != null ) {
                    if ( ITEM.getHolidayAlarmStop()) {
                        holidayAlarmStop.setVisibility(View.VISIBLE);
                    } else {
                        holidayAlarmStop.setVisibility(View.INVISIBLE);
                    }
                }
                
                // アラームONの情報をビューに設定する
                CheckBox alarmOn = (CheckBox)view.findViewById(R.id.alarmOn);
                if ( alarmOn != null ) {
                    alarmOn.setChecked(ITEM.getAlarmOn());
                }
                // アラームON チェックボックス クリックリスナー
                OnClickListener alarmOnClickListener = new OnClickListener() {
                    public void onClick(View view) {
                        // チェックボックスのクリック状態をITEMに登録する
                        CheckBox checkBox = (CheckBox)view;
                        ITEM.setAlarmOn(checkBox.isChecked());
                        updateAlarmTableFromAlarmInfo(ITEM);
                        
                        // 次アラームの登録・更新
                        NextAlarm tmp = getNextAlarmFromAlarmTable();
                        setNextAlarm(tmp);
                    }
                };
                alarmOn.setOnClickListener(alarmOnClickListener);
            }
            return view;
        }
    }
    
    /***************************************
     * アラームリスト表示更新
     ****************************************/
    private void updateListView(ArrayList<AlarmInfo> list) {
        // アラーム情報からアラームリストを生成
        ADAPTER = new alarmListAdapter(this, R.layout.alramlist_row, list);
        ListView alarmList = (ListView)findViewById(R.id.alarmList);
        alarmList.setAdapter(ADAPTER);        
    }

    /***************************************
     * 次アラームの表示更新
     * @param alarm 次アラーム
     ****************************************/
    private void updateNextAlarm(NextAlarm alarm) {
        final SimpleDateFormat sdf_day = new SimpleDateFormat("yyyy年M月d日");
        final SimpleDateFormat sdf_time = new SimpleDateFormat("HH:mm");
        String day, time, dayOfWeek = null;

        day = sdf_day.format(alarm.getAlarmCal().getTime());
        time = sdf_time.format(alarm.getAlarmCal().getTime());
        
        switch (alarm.getAlarmCal().get(Calendar.DAY_OF_WEEK)) {
            case Calendar.MONDAY:   dayOfWeek = "(月)"; break;
            case Calendar.TUESDAY:  dayOfWeek = "(火)"; break;
            case Calendar.WEDNESDAY:dayOfWeek = "(水)"; break;
            case Calendar.THURSDAY: dayOfWeek = "(木)"; break;
            case Calendar.FRIDAY:   dayOfWeek = "(金)"; break;
            case Calendar.SATURDAY: dayOfWeek = "(土)"; break;
            case Calendar.SUNDAY:   dayOfWeek = "(日)"; break;
            default: {} // ここには来ない
        }
        
        // 次アラームエリアに設定
        TextView nextAlarmTxt = (TextView)findViewById(R.id.nextAlarmTime_txt);
        nextAlarmTxt.setText(day + dayOfWeek + "　" + time);
        
        // 次アラームの更新を通知する
        String message = "次のアラームを\n" + day + time + "\nに設定しました。";
        Toast.makeText(AlarmActivity.this, message, Toast.LENGTH_SHORT)
                .show();
    }
  
    /***************************************
     * 次アラーム ゲッター
     ****************************************/
    public NextAlarm getNextAlarm() {
        return m_nextAlarm;
    }
    
    /***************************************
     * 次アラーム セッター
     * <p>
     * 次アラーム情報の更新に伴い、アラームの登録 及び 表示更新を行う
     * </p>
     ****************************************/
    public void setNextAlarm(NextAlarm nextAlarm) {
      //nextAlarmがあるかどうか
        if (nextAlarm == null || nextAlarm.getAlarmCal() == null || nextAlarm.getAlarmInfo() == null){
            //次のアラームが無ければここに入る
            Log.d("setNextAlarm", "nextAlarmはありません");
            // 次アラームエリアを空白に設定
            TextView nextAlarmTxt = (TextView)findViewById(R.id.nextAlarmTime_txt);
            nextAlarmTxt.setText("");

            // 次アラームの更新を通知する
            String message = "次のアラームはありません。";
            Toast.makeText(AlarmActivity.this, message, Toast.LENGTH_SHORT)
            .show();
            return;
        }
        m_nextAlarm = nextAlarm;
        // 次アラームの登録
        startAlarm(m_nextAlarm);
        
        // 次アラームの表示更新
        updateNextAlarm(m_nextAlarm);

    }
    
    /***************************************
     * 次アラーム情報
     ****************************************/
    public class NextAlarm {
        private AlarmInfo m_alarmInfo;
        private Calendar m_alarmCal;
        
        public NextAlarm(Context context) {
            setAlarmInfo(new AlarmInfo(context));
            setAlarmCal(Calendar.getInstance());
        }
        
        public AlarmInfo getAlarmInfo() {
            return m_alarmInfo;
        }

        public void setAlarmInfo(AlarmInfo alarmInfo) {
            this.m_alarmInfo = alarmInfo;
        }

        public Calendar getAlarmCal() {
            return m_alarmCal;
        }

        public void setAlarmCal(Calendar calendar) {
            this.m_alarmCal = calendar;
        }        
    }
}